﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Visual_App
{
    /// <summary>
    /// Spezielle Serialisierung für TimeSeries
    /// </summary>
    public partial class _frm_TimeSeries : Form
    {
        private bool allow_close = false;
        private delegate void _del_REFR(IVisual.I_Network _N);

        private double[,] windowDelimiter = new double[2, 2] { { 0, 0 }, { 0, 0 } };
        private double[,] predictionDelimiter = new double[2, 2] { { 0, 0 }, { 0, 0 } };

        private double[] data = null;
        public int windowSize = 5;
        public int predictionSize = 1;

        private int solutionSize = 0;
        private double[,] solution = null;
        private float[] networkInput = null;

        public _frm_TimeSeries()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Zeichnet ein Netz
        /// </summary>
        /// <param name="_N">Netz welches gezeichnet werden soll</param>
        public void REFR(IVisual.I_Network _N)
        {
            if (this.InvokeRequired)
                this.Invoke(new _del_REFR(this.REFR), _N);
            else
            {
                // transformationsfaktor
                double factor = 1.7 / this._chart.RangeY.Length;
                double yMin = this._chart.RangeY.Min;
                IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "factor=" + factor + " yMin=" + yMin);

                for (int i = 0, n = data.Length - windowSize; i < n; i++)
                {
                    // aktuelle "fensterwerte" als Netzeingabe festlegen
                    for (int j = 0; j < windowSize; j++)
                    {
                        networkInput[j] = (float)((data[i + j] - yMin) * factor - 0.85);
                        IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "networkInput[" + j + "]=" + networkInput[j]);
                    }

                    // Lösung berechnen
                    solution[i, 1] = (_N.Compute(networkInput)[0] + 0.85) / factor + yMin;
                    IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "solution[" + i + ", 1]=" + solution[i, 1]);

                    // fehler berechnen
                    /*if (i >= n - predictionSize)
                    {
                        predictionError += Math.Abs(solution[i, 1] - data[windowSize + i]);
                    }
                    else
                    {
                        learningError += Math.Abs(solution[i, 1] - data[windowSize + i]);
                    }*/
                }
                
                this._chart.UpdateDataSeries("solution", solution);
            }
        }

        /// <summary>
        /// Initialisert das Control aus den cfg daten
        /// </summary>
        /// <param name="_data">cfg daten</param>
        public void INIT(double[] _data)
        {
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "start");

            this.data = _data;

            solutionSize = data.Length - windowSize;
            solution = new double[solutionSize, 2];
            networkInput = new float[windowSize];

            // x-werte berechnen, welche dem Netz präsentiert werden
            for (int j = 0; j < solutionSize; j++)
            {
                solution[j, 0] = j + windowSize;
            }

            double[,] dataToShow = new double[data.Length, 2];
            for (int j = 0; j < data.Length; j++)
            {
                dataToShow[j, 0] = j;
                dataToShow[j, 1] = data[j];
            }
            this._chart.RangeX = new AForge.Range(0, data.Length - 1);// new AForge.DoubleRange(0, data.Length - 1);
            this._chart.UpdateDataSeries("data", dataToShow);
            UpdateDelimiters();

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
        }

        /// <summary>
        /// initialisiert chart
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _frm_Approx_Load(object sender, EventArgs e)
        {
            this._chart.AddDataSeries("data", Color.Red, AForge.Controls.Chart.SeriesType.Dots, 5);
            this._chart.AddDataSeries("solution", Color.Blue, AForge.Controls.Chart.SeriesType.ConnectedDots, 5);
            this._chart.AddDataSeries("window", Color.LightGray, AForge.Controls.Chart.SeriesType.Line, 1, false);
            this._chart.AddDataSeries("prediction", Color.Gray, AForge.Controls.Chart.SeriesType.Line, 1, false);
        }

        /// <summary>
        /// Aktualisiert die grenzen in chart
        /// </summary>
        private void UpdateDelimiters()
        {
            // window
            windowDelimiter[0, 0] = windowDelimiter[1, 0] = windowSize;
            windowDelimiter[0, 1] = this._chart.RangeY.Min;
            windowDelimiter[1, 1] = this._chart.RangeY.Max;
            this._chart.UpdateDataSeries("window", windowDelimiter);

            // prediction
            predictionDelimiter[0, 0] = predictionDelimiter[1, 0] = data.Length - 1 - predictionSize;
            predictionDelimiter[0, 1] = this._chart.RangeY.Min;
            predictionDelimiter[1, 1] = this._chart.RangeY.Max;
            this._chart.UpdateDataSeries("prediction", predictionDelimiter);
        }

        /// <summary>
        /// Schließt die Form via programm
        /// </summary>
        public void DO_CLOSE()
        {
            this.allow_close = true;
            this.Close();
        }

        private void _frm_Approx_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = !this.allow_close;
        }
    }
}
